﻿using System;
using BuzzX8.Xmpp.Properties;

namespace BuzzX8.Xmpp.Connectivity
{
    public abstract class XmppConnectionBase : XmppConnection, IDisposable
    {
        private ConnectionSettings settings;
        private IXmppChannel channel;
        private IXmppStream stream;
        private bool disposed = false;

        protected XmppConnectionBase()
        {
        }

        protected XmppConnectionBase(ConnectionSettings settings)
        {
            if (settings == null) throw new ArgumentNullException("settings");
            this.settings = settings;
        }

        protected IXmppChannel Channel
        {
            get { return channel; }
        }

        public override bool IsOpen
        {
            get { return channel != null && channel.IsOpen; }
        }

        public override ConnectionSettings Settings
        {
            get { return settings; }
        }
        
        public override void Open()
        {
            ThrowExceptionIfDisposed();
            ThrowExceptionIfAlreadyOpen();
            OnBeforeOpen();
            OpenChannel();
            OnAfterOpen();
        }

        private void ThrowExceptionIfAlreadyOpen()
        {
            if (IsOpen) throw new InvalidOperationException(Resources.XmppConnectionAlreadyOpenExceptionMessage);
        }

        protected virtual void OnBeforeOpen()
        { }

        protected virtual void OpenChannel()
        {
            channel = CreateChannel(settings);
            channel.Open();
        }

        protected abstract IXmppChannel CreateChannel(ConnectionSettings settings);

        protected virtual void OnAfterOpen()
        { }

        public override void Close()
        {
            if (!IsOpen) return;
            OnBeforeClose();
            CloseChannel();
            OnAfterClose();
        }

        protected virtual void OnBeforeClose()
        { }

        protected virtual void CloseChannel()
        {
            channel.Close();
        }

        protected virtual void OnAfterClose()
        { }

        public override IXmppStream GetStream()
        {
            ThrowExceptionIfDisposed();
            ThrowExceptionIfClosed();
            if (stream == null) stream = new XmppStream(channel);
            return stream;
        }

        protected void ThrowExceptionIfDisposed()
        {
            if (disposed) throw new ObjectDisposedException("XmppConncetion");
        }

        protected void ThrowExceptionIfClosed()
        {
            if(!IsOpen) throw new InvalidOperationException();
        }

        public virtual void Dispose()
        {
            Close();
            disposed = true;
            if (channel is IDisposable) ((IDisposable) channel).Dispose();
        }
    }
}
